using HotChocolate.Fusion.Events;
using HotChocolate.Fusion.Events.Contracts;
using HotChocolate.Fusion.Extensions;
using HotChocolate.Fusion.Validators;
using HotChocolate.Types;
using static HotChocolate.Fusion.Logging.LogEntryHelper;

namespace HotChocolate.Fusion.SourceSchemaValidationRules;

/// <summary>
/// Even if the <c>@provides(fields: "…")</c> argument is well-formed syntactically, the selected
/// fields must actually exist on the return type of the field. Invalid field references—e.g.,
/// selecting non-existent fields, referencing fields on the wrong type, or incorrectly omitting
/// required nested selections—lead to a <c>PROVIDES_INVALID_FIELDS</c> error.
/// </summary>
/// <seealso href="https://graphql.github.io/composite-schemas-spec/draft/#sec-Provides-Invalid-Fields">
/// Specification
/// </seealso>
internal sealed class ProvidesInvalidFieldsRule : IEventHandler<OutputFieldEvent>
{
    public void Handle(OutputFieldEvent @event, CompositionContext context)
    {
        var (field, _, schema) = @event;
        var validator = new SelectionSetValidator(schema);

        if (field.ProvidesInfo is { SelectionSet: { } selectionSet } providesInfo)
        {
            var errors = validator.Validate(selectionSet, field.Type.AsTypeDefinition());

            if (errors.Any())
            {
                context.Log.Write(
                    ProvidesInvalidFields(
                        providesInfo.Directive,
                        field,
                        schema,
                        errors));
            }
        }
    }
}
